#!/usr/bin/env python.exe
#coding:utf-8
"""
三种算法: DES, 3DES, AES.  越往后越建议使用

0. 区别
加解密的三个库: M2Crypto, Pycrypto, pyDes. 2017年新的库: cryptography, 目标是python标准加密库
        pip install M2Crypto
        pip install Pycrypto
        pip install pyDes

跨平台建议使用Pycrypto, 但是效率上不如C库的M2Crypto. 而pyDes效率最慢.

1. 用法
Pycrypto用法:    参考:  http://blog.csdn.net/yyhustim/article/details/8539065
    from Crypto.Cipher import DES3
    obj = DES3.new('abcdefgh', DES3.MODE_ECB)
    plain = 'text'
    # 加密
    encrypt = obj.encrypt( plain )
    # 机密
    plain = obj.decrypt( encrypt )

M2Crypto用法:
    from M2Crypto.EVP import Cipher

2. 安装
Windows x64版本:
    wget https://pypi.python.org/pypi/M2CryptoWin64/0.21.1-3
    jieya M2CryptoWin64-0.21.1-3.tar.gz; cd M2CryptoWin64-0.21.1-3;
    pip install --egg M2CryptoWin64
"""
from M2Crypto.EVP import Cipher
import base64

def encrypt_3des(key, text, usebase64=False):
    cipher = Cipher(alg='des_ede3_ecb', key=key, op=1, iv='\0'*16)
    s = cipher.update(text)
    if usebase64:
        return base64.b64encode( s + cipher.final() )
    else:
        return s + cipher.final()

def decrypt_3des(key, text, usebase64=False):
    if usebase64:
        text = base64.b64decode( text )
    cipher = Cipher(alg='des_ede3_ecb', key=key, op=0, iv='\0'*16)
    s = cipher.update(text)
    return s + cipher.final()

def encrypt_aes(key, text, usebase64=False):
    """ aes比3des: 加解密速度快, 资源消耗低, 安全级别高
    param:
        key: 密钥, 32个字符
        标准Base64编码会出现字符+和/，在URL中不能作为参数，而urlsafe的base64编码，其实是把字符+和/分别变成-和_
    """
    if len(key) != 32:
        key = key * (32/len(key)) + '0'*(32%len(key))
    cipher = Cipher(alg='aes_256_ecb', key=key, op=1, iv='\0'*16)
    s = cipher.update(text)
    if usebase64:
        return base64.urlsafe_b64encode( s + cipher.final() )
    else:
        return s + cipher.final()

def decrypt_aes(key, text, usebase64=False):
    """ aes比3des: 加解密速度快, 资源消耗低, 安全级别高
    param:
        key: 密钥, 32个字符
        标准Base64编码会出现字符+和/，在URL中不能作为参数，而urlsafe的base64编码，其实是把字符+和/分别变成-和_
    """
    if len(key) != 32:
        key = key * (32/len(key)) + '0'*(32%len(key))
    if usebase64:
        text = base64.urlsafe_b64decode( text )
    cipher = Cipher(alg='aes_256_ecb', key=key, op=0, iv='\0'*16)
    s = cipher.update(text)
    return s + cipher.final()

def read( filepath ):
    with open(filepath, 'r') as f:
        text = f.read() #读取整个文件，字符串显示
        return text

def write( filepath, byte ):
    with open(filepath, 'wb') as f:
        f.write( byte )

if __name__ == "__main__":
    def init_args():
        import argparse
        parser = argparse.ArgumentParser()
        parser.add_argument('-d', action='store_true', help='for decrypt', required=False, default=False, dest='is_decrypt')
        parser.add_argument('-k', metavar='<key_file>', type=str, help='key file', required=True, dest='key_file')
        parser.add_argument('-i', metavar='<input_file>', type=str, help='input file(text)', required=True, dest="input_file")
        parser.add_argument('-o', metavar='<output_file>', type=str, help='output file(encrypt)', required=False, dest="output_file")
        #print dir(parser)
        #parser.print_help()
        return parser.parse_args()

    def main():
        args = init_args()
        key = read( args.key_file )
        text = read( args.input_file )
    
        if args.is_decrypt:
            plaintext = decrypt_3des(key, text)
            print plaintext
            if args.output_file is not None: write( args.output_file, plaintext )
        else:
            encrypt_text = encrypt_3des(key, text)
            print encrypt_text.encode('hex')
            if args.output_file is not None: write( args.output_file, encrypt_text )

    def test_3des():
        jdata = {'data': 'R8wQ/2HFbOBqtCDjJrIyRMIE/y4larbGQKJUZOLO9Lzjzc7yCuYM+A==', 'time': '1492518984.96'}
        key = jdata['time']
        text = jdata['data']
        print "key:%s, len:%s, text:%s" % ( key, len(key), text )
        encrypt_text = encrypt_3des(key, text)
        print "encrypt_text:%s, len:%s" % ( encrypt_text.encode('hex'), len(encrypt_text) )
        plaintext = decrypt_3des(key, encrypt_text)
        print "plaintext:%s" % (plaintext)

        print '\n######### test use base64 ##########'
        key = key
        text = text
        print "key:%s, len:%s, text:%s" % ( key, len(key), text )
        encrypt_text = encrypt_3des(key, text, True)
        print "encrypt_text:%s, len:%s" % ( encrypt_text.encode('hex'), len(encrypt_text) )
        plaintext = decrypt_3des(key, encrypt_text, True)
        print "plaintext:%s" % (plaintext)

    def test_aes():
        jdata = {'data': 'this is a text to encrypt.', 'time': '1492518984.96'}
        key = (jdata['time']*3)[0:32]
        text = jdata['data']
        print "key:%s, len:%s, text:%s" % ( key, len(key), text )
        encrypt_text = encrypt_aes(key, text)
        print "encrypt_text:%s, len:%s" % ( encrypt_text.encode('hex'), len(encrypt_text) )
        plaintext = decrypt_aes(key, encrypt_text)
        print "plaintext:%s" % (plaintext)

        print '\n######### test use base64 ##########'
        key = key
        text = text
        print "key:%s, len:%s, text:%s" % ( key, len(key), text )
        encrypt_text = encrypt_aes(key, text, True)
        print "encrypt_text:%s, len:%s" % ( encrypt_text.encode('hex'), len(encrypt_text) )
        plaintext = decrypt_aes(key, encrypt_text, True)
        print "plaintext:%s" % (plaintext)

if __name__ == "__main__":
    def Usage():
        print u"""
        python ./{0} main -k key_file -i text_file -o encrypt_file
        python ./{0} main -k key_file -i encrypt_file -o text_file -d
        python ./{0} test_3des
        python ./{0} test_aes
        """.format(__file__)
    import sys
    if len(sys.argv) == 1: Usage(), sys.exit()
    function = sys.argv[1]
    del sys.argv[1]
    eval(function)()
